/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = http_chunk.c                                       */
/*  NOTE      =                                                    */
/*  DATE      = 2015/03/04 by geyh                                 */
/*******************************************************************/
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <frbyte_buffer.h>

#define CHUNKED_MAX_LEN  128

#include "frcomm.h"
#include "frmem.h"
#include "frstr.h"
#include "http_chunk.h"

/*******************************************************************************
*  FUNC     :  16进制转10进制
*  ARGS     :  p_src 16进制数据
*  RTN      :  0        (失败)
*              1        (10进制数)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __htoi(unsigned char *p_src, unsigned long *p_ul_value)
{
    int i_ret = 0;
    char *p_end=NULL;
    
    if(p_src == NULL || p_ul_value == NULL)
        return i_ret;
    
    *p_ul_value = strtoul((const char *)p_src, &p_end, 16);
    
    if(p_end == NULL || *p_end == '\0')
        i_ret = 1;
    return i_ret;
}

/*******************************************************************************
*  FUNC     :  查找关键数据串在长数据中出现的位置
*  ARGS     :  p_data           (长数据指针)
*           :  i_data_length    (搜索最大长度)
*           :  p_key            (关键字指针)
*           :  i_key_length     (i_key_length)
*           :  p_position       (p_position)
*  RTN      :  0                (未找到)
*           :  1                (成功)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __find_key(unsigned char *p_data,int i_data_length, char *p_key,int i_key_length,int *p_position)
{
    int i_index = *p_position;
    if(p_key == NULL || i_index<0)
    {
        return 0;
    }
    for(; i_index <= i_data_length-i_key_length; i_index++)
    {
        if( memcmp(p_data+i_index, p_key, i_key_length) == 0 )
        {
            *p_position = i_index;
            return 1;
        }
    }
    return 0;
}

/*******************************************************************************
*  FUNC     :  对HTTP的chunked消息进行合块
*  ARGS     :  p_data               (待处理数据)
*           :  i_data_length        (数据长度)
*           :  pp_dest              (返回合块后的数据，)
*           :  p_ul_dest_length     (4合块长度)
*  RTN      :  0                    (数据未完全失败)
*           :  -1                   (其他失败)
*           :  1                    (成功)
*  NOTE     : p_dest调用函数前为NULL,*p_ul_dest_length调用函数前0
*******************************************************************************/
int de_chunked(unsigned char *p_data,int i_data_length, char **pp_dest,unsigned long *p_ul_dest_length)
{
    unsigned char chunked_hex[CHUNKED_MAX_LEN + 1];     // 十六进制的块长度
    unsigned long ul_chunked_len = 0;                   // 块长度
    int i_ret = 0;
    int begin = 0;
    int i_len = 0;
    Frstr *p_st_str=NULL;
    
    if(p_data == NULL || p_ul_dest_length == NULL || i_data_length <= 0)
        return -1;
    *p_ul_dest_length = 0;
    
    //获得当前块长度
    i_len = 0;
    i_ret = __find_key(p_data, i_data_length, (char *)"\r\n",2,&i_len);
    if (i_ret == 0)    //信息不完整
        return 0;
    
    if( (unsigned int)i_len >= sizeof(chunked_hex) )
        return -1;
    
    memcpy(chunked_hex, p_data, i_len);
    chunked_hex[i_len] = '\0';
    i_ret = __htoi(chunked_hex, &ul_chunked_len);
    if(i_ret == 0)
        return -1;
    
    //数据解码结束
    if(ul_chunked_len ==0)
        return 1;
    
    //移动到当前块数据段
    begin += (i_len + 2);
    
    //数据长度不够
    if(ul_chunked_len > (unsigned int)i_data_length)
        return 0;
    
    if(pp_dest)
    {
        p_st_str = frstr_new(512);
        if(p_st_str == NULL)
            goto ERROR;
    }
    
    while(1)
    {
        //判断格式
        if (memcmp(p_data+begin+ul_chunked_len,"\r\n",2) != 0)
            goto DATA_NOT_END;
        
        if(pp_dest)
            //获得当前块数据
            frstr_append(p_st_str, (const char *)(p_data+begin), ul_chunked_len);
        
        //移动到下一块块长度
        begin = begin + ul_chunked_len + 2;
        
        //获得当前块长度
        if(i_data_length <= begin +2)
            goto DATA_NOT_END;
        i_len = 0;
        i_ret = __find_key(p_data+begin, i_data_length - begin,(char *)"\r\n",2,&i_len);
        if (i_ret == 0)    //信息不完整
            goto DATA_NOT_END;
        
        if( (unsigned int)i_len >= sizeof(chunked_hex) )
            goto ERROR;
        memcpy(chunked_hex, p_data+begin, i_len);
        chunked_hex[i_len] = '\0';
        i_ret = __htoi(chunked_hex, &ul_chunked_len);
        if(i_ret == 0)
            goto ERROR;
        
        //移动到当前块数据段
        begin += (i_len + 2);
        if(ul_chunked_len > (unsigned int)(i_data_length - begin))
            goto DATA_NOT_END;
        
        if(ul_chunked_len ==0)
            break;
    }
    
    *p_ul_dest_length = FRSTR_LEN(p_st_str);
    if(pp_dest)
    {
        frstr_append(p_st_str, "\0", 1);
        *pp_dest = frstr_free2(&p_st_str);
    }
    else
    {
        if(p_st_str)
            frstr_free(&p_st_str);
    }
    
    return 1;
    
DATA_NOT_END:
    if(p_st_str)
        frstr_free(&p_st_str);
    return 0;
    
ERROR:
    if(p_st_str)
        frstr_free(&p_st_str);
    
    return -1;
}

/*
 * \func 获取chunk整个数据块
 *
 * \p_packet                (Frbyte_Packet *)数据块内容
 * \p_comm_session          (Http_comm_session *)session结构体
 *
 * \return                  HTTP_CHUNK_ERR: 失败   HTTP_CHUNK_OK:结束   HTTP_CHUNK_TODO:未结束
 */
int http_chunk_data_get(Frbyte_Packet *p_packet, Http_comm_session *p_comm_session)
{
    char arr_chunk_end[6];
    char arr_chunk_tmp[16];
    int i_chunk_left_len = 0;
    int i_tmp_pos = 0;
    unsigned long ul_chunk_len = 0;
    unsigned int ui_one_chunk_len = 0;
    int i_find_remain = 0;
    if (p_packet == NULL || p_comm_session == NULL)
        return HTTP_CHUNK_ERR;
    i_chunk_left_len = p_comm_session->i_chunk_left_len;
    while (bytebuff_remaining(p_packet))
    {
        // chunk长度不完整
        if (i_chunk_left_len == -1)
        {
            // 寻找chunk 长度 最大只匹配前 17 位
            i_find_remain = bytebuff_remaining(p_packet) > (int)sizeof(arr_chunk_tmp) + 1 ? (int)sizeof(arr_chunk_tmp) + 1 : bytebuff_remaining(p_packet);
            if (bytebuff_memstr(p_packet, bytebuff_getPosition(p_packet), bytebuff_getPosition(p_packet) + i_find_remain, "\r\n", 2, &i_tmp_pos) == FR_FAIL)
            {
                //如果 长度位 超过 规定大小 认为丢包 不做处理
                if(bytebuff_remaining(p_packet) > (int)sizeof(arr_chunk_tmp) - 1)
                    return HTTP_CHUNK_ERR;

                //长度位不足 返回 HTTP_CHUNK_TODO
                return HTTP_CHUNK_TODO;
            }
            
            memset(arr_chunk_tmp, 0x00, sizeof(arr_chunk_tmp));
            if (bytebuff_peek_bytes(p_packet, arr_chunk_tmp, (unsigned int)(i_tmp_pos - bytebuff_getPosition(p_packet))) == FR_FAIL)
                return HTTP_CHUNK_ERR;

            if(!__htoi((unsigned char *)arr_chunk_tmp, &ul_chunk_len))
                return HTTP_CHUNK_ERR;
            
            if(ul_chunk_len == 0)
                return HTTP_CHUNK_OK;
            
            // chunk长度位 + \r\n + chunk +\r\n
            ui_one_chunk_len = i_tmp_pos - bytebuff_getPosition(p_packet) + 2 + ul_chunk_len + 2;
            if (bytebuff_remaining(p_packet) < (int)ui_one_chunk_len)
            {
                p_comm_session->i_chunk_left_len = ui_one_chunk_len - bytebuff_remaining(p_packet);
                bytebuff_skip(p_packet, (unsigned int)bytebuff_remaining(p_packet));
                return HTTP_CHUNK_TODO;
            }
            else if (bytebuff_remaining(p_packet) == (int)ui_one_chunk_len)
            {
                //chunk 块 + \r\n
                bytebuff_skip(p_packet, ui_one_chunk_len - 2);
                if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                    return HTTP_CHUNK_ERR;
                
                if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                    return HTTP_CHUNK_ERR;
                
                p_comm_session->i_chunk_left_len = 0;
                return HTTP_CHUNK_TODO;
            }
            else
            {
                //chunk 块 + \r\n
                bytebuff_skip(p_packet, ui_one_chunk_len - 2);
                if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                    return HTTP_CHUNK_ERR;
    
                if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                    return HTTP_CHUNK_ERR;
                
                // 最后一个chunk
                if (bytebuff_remaining(p_packet) == 5)
                {
                    memset(arr_chunk_end, 0x00, sizeof(arr_chunk_end));
                    if (bytebuff_peek_bytes(p_packet, arr_chunk_end, 5) == FR_FAIL)
                        return HTTP_CHUNK_ERR;
                    
                    if (strncmp(arr_chunk_end, "0\r\n\r\n", 5) == 0)
                        return HTTP_CHUNK_OK;
                }
                
                i_chunk_left_len = 0;
                continue;
            }
        }
        else if (i_chunk_left_len == 0)
        {
            if (bytebuff_remaining(p_packet) == 5)
            {
                if( bytebuff_peek_bytes(p_packet, arr_chunk_end, 5) == FR_FAIL)
                    return HTTP_CHUNK_ERR;
    
                if (strncmp(arr_chunk_end, "0\r\n\r\n", 5) == 0)
                    return HTTP_CHUNK_OK;
            }
            
            // 寻找chunk 长度 最大只匹配前 17 位
            i_find_remain = bytebuff_remaining(p_packet) > (int)sizeof(arr_chunk_tmp) + 1 ? (int)sizeof(arr_chunk_tmp) + 1 : bytebuff_remaining(p_packet);
            if (bytebuff_memstr(p_packet, bytebuff_getPosition(p_packet), bytebuff_getPosition(p_packet) + i_find_remain, "\r\n", 2, &i_tmp_pos) == FR_FAIL)
            {
                //如果 长度位 超过 规定大小 认为丢包 不做处理
                if(bytebuff_remaining(p_packet) > (int)sizeof(arr_chunk_tmp) - 1)
                    return HTTP_CHUNK_ERR;
        
                //长度位不足 返回 HTTP_CHUNK_TODO
                p_comm_session->i_chunk_left_len = -1;
                return HTTP_CHUNK_TODO;
            }
            
            memset(arr_chunk_tmp, 0x00, sizeof(arr_chunk_tmp));
            if (bytebuff_peek_bytes(p_packet, arr_chunk_tmp, i_tmp_pos - bytebuff_getPosition(p_packet)) == FR_FAIL)
                return HTTP_CHUNK_ERR;
    
            if(!__htoi((unsigned char *)arr_chunk_tmp, &ul_chunk_len))
                return HTTP_CHUNK_ERR;
    
            if(ul_chunk_len == 0)
                return HTTP_CHUNK_OK;
            
            // chunk长度位 + \r\n + chunk +\r\n
            ui_one_chunk_len = i_tmp_pos - bytebuff_getPosition(p_packet) + 2 + ul_chunk_len + 2;
            if (bytebuff_remaining(p_packet) < (int)ui_one_chunk_len)
            {
                p_comm_session->i_chunk_left_len = ui_one_chunk_len - bytebuff_remaining(p_packet);
                bytebuff_skip(p_packet, bytebuff_remaining(p_packet));
                return HTTP_CHUNK_TODO;
            }
            else if (bytebuff_remaining(p_packet) == (int)ui_one_chunk_len)
            {
                //chunk 块 + \r\n
                bytebuff_skip(p_packet, ui_one_chunk_len - 2);
                if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                    return HTTP_CHUNK_ERR;
        
                if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                    return HTTP_CHUNK_ERR;
        
                p_comm_session->i_chunk_left_len = 0;
                return HTTP_CHUNK_TODO;
            }
            else
            {
                //chunk 块 + \r\n
                bytebuff_skip(p_packet, ui_one_chunk_len - 2);
                if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                    return HTTP_CHUNK_ERR;
        
                if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                    return HTTP_CHUNK_ERR;
        
                // 最后一个chunk
                if (bytebuff_remaining(p_packet) == 5)
                {
                    memset(arr_chunk_end, 0x00, sizeof(arr_chunk_end));
                    if (bytebuff_peek_bytes(p_packet, arr_chunk_end, 5) == FR_FAIL)
                        return HTTP_CHUNK_ERR;
            
                    if (strncmp(arr_chunk_end, "0\r\n\r\n", 5) == 0)
                        return HTTP_CHUNK_OK;
                }
        
                i_chunk_left_len = 0;
                continue;
            }
        }
        else // i_chunk_left_len >= 1
        {
            if (bytebuff_remaining(p_packet) < i_chunk_left_len)
            {
                p_comm_session->i_chunk_left_len -= bytebuff_remaining(p_packet);
                bytebuff_skip(p_packet, bytebuff_remaining(p_packet));
                return HTTP_CHUNK_TODO;
            }
            else if (bytebuff_remaining(p_packet) == i_chunk_left_len)
            {
                //chunk 块 + \r\n
                if(i_chunk_left_len == 1)
                {
                    bytebuffer_backspace(p_packet, 1);
                    if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                        return HTTP_CHUNK_ERR;
    
                    if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                        return HTTP_CHUNK_ERR;
    
                    p_comm_session->i_chunk_left_len = 0;
                    return HTTP_CHUNK_TODO;
                }
                else
                {
                    bytebuff_skip(p_packet, i_chunk_left_len - 2);
                    if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                        return HTTP_CHUNK_ERR;
    
                    if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                        return HTTP_CHUNK_ERR;
    
                    p_comm_session->i_chunk_left_len = 0;
                    return HTTP_CHUNK_TODO;
                }
            }
            else // bytebuff_remaining(p_packet) >= 2
            {
                bytebuff_skip(p_packet, i_chunk_left_len - 2);
                if(bytebuff_get_bytes(p_packet, arr_chunk_tmp, 2) == FR_FAIL)
                    return HTTP_CHUNK_ERR;
    
                if(strncmp(arr_chunk_tmp, "\r\n", 2)!= 0)
                    return HTTP_CHUNK_ERR;
    
                // 最后一个chunk
                if (bytebuff_remaining(p_packet) == 5)
                {
                    memset(arr_chunk_end, 0x00, sizeof(arr_chunk_end));
                    if (bytebuff_peek_bytes(p_packet, arr_chunk_end, 5) == FR_FAIL)
                        return HTTP_CHUNK_ERR;
                    
                    if (strncmp(arr_chunk_end, "0\r\n\r\n", 5) == 0)
                        return HTTP_CHUNK_OK;
                }
                
                i_chunk_left_len = 0;
                continue;
            }
        }
    }
    
    p_comm_session->i_chunk_left_len = 0;
    return HTTP_CHUNK_OK;
}

/*
 * \func 跳过chunk整个数据块
 *
 * \p_data                  (char *)数据块内容
 * \p_comm_session          (Http_comm_session *)session结构体
 *
 * \return                  HTTP_CHUNK_ERR: 失败   HTTP_CHUNK_OK:结束   HTTP_CHUNK_TODO:未结束
 */
int http_chunk_data_skip(char *p_data, unsigned int ui_data_len, Http_comm_session *p_comm_session)
{
    unsigned char arr_chunk_len[16];
    char *p_chunk_crlf = NULL;
    int i_chunk_left_len = 0;
    unsigned long ul_chunk_len = 0;
    unsigned int ui_one_chunk_len = 0;
    
    if (p_data == NULL || p_comm_session == NULL)
        return HTTP_CHUNK_ERR;
    
    i_chunk_left_len = p_comm_session->i_chunk_left_len;
    while (ui_data_len > 0)
    {
        // chunk长度不完整
        if (i_chunk_left_len == -1)
        {
            p_chunk_crlf = strstr(p_data, "\r\n");
            if (p_chunk_crlf == NULL)
            {
                //chunk 长度位过长
                if (strlen(p_comm_session->arr_tmp) + ui_data_len > sizeof(p_comm_session->arr_tmp) - 1)
                {
                    memset(p_comm_session->arr_tmp, 0x00, sizeof(p_comm_session->arr_tmp));
                    return HTTP_CHUNK_ERR;
                }
                strcat(p_comm_session->arr_tmp, p_data);
                return HTTP_CHUNK_TODO;
            }
            
            //长度过长
            if (strlen(p_comm_session->arr_tmp) + (p_chunk_crlf - p_data) > sizeof(p_comm_session->arr_tmp) - 1)
                return HTTP_CHUNK_ERR;
            
            strncat(p_comm_session->arr_tmp, p_data, p_chunk_crlf - p_data);
            if(!__htoi((unsigned char *)p_comm_session->arr_tmp, &ul_chunk_len))
                return HTTP_CHUNK_ERR;
    
            if(ul_chunk_len == 0)
                return HTTP_CHUNK_OK;
            
            memset(p_comm_session->arr_tmp, 0x00, sizeof(p_comm_session->arr_tmp));
            
            // chunk长度位 + \r\n + chunk + \r\n
            ui_one_chunk_len = p_chunk_crlf - p_data + 2 + ul_chunk_len + 2;
            if (ui_data_len < ui_one_chunk_len)
            {
                p_comm_session->i_chunk_left_len = ui_one_chunk_len - ui_data_len;
                return HTTP_CHUNK_TODO;
            }
            else if (ui_data_len == ui_one_chunk_len)
            {
                p_comm_session->i_chunk_left_len = 0;
                return HTTP_CHUNK_TODO;
            }
            else
            {
                p_data += ui_one_chunk_len;
                ui_data_len -= ui_one_chunk_len;
                // 最后一个chunk
                if (ui_data_len == 5 && strncmp(p_data, "0\r\n\r\n", 5) == 0)
                    return HTTP_CHUNK_OK;
                i_chunk_left_len = 0;
                continue;
            }
        }
        else if (i_chunk_left_len == 0)
        {
            if (ui_data_len == 5 && strncmp(p_data, "0\r\n\r\n", 5) == 0)
                return HTTP_CHUNK_OK;
            
            // chunk长度后的\r\n
            p_chunk_crlf = strstr(p_data, "\r\n");
            if (p_chunk_crlf == NULL)
            {
                if (ui_data_len > sizeof(p_comm_session->arr_tmp) - 1)
                    return HTTP_CHUNK_ERR;
                memset(p_comm_session->arr_tmp, 0x00, sizeof(p_comm_session->arr_tmp));
                memcpy(p_comm_session->arr_tmp, p_data, ui_data_len);
                p_comm_session->i_chunk_left_len = -1;
                return HTTP_CHUNK_TODO;
            }
            
            memset(arr_chunk_len, 0x00, sizeof(arr_chunk_len));
            
            //长度判断
            if(p_chunk_crlf - p_data >  (unsigned int)sizeof(arr_chunk_len) - 1)
                return HTTP_CHUNK_ERR;
            
            memcpy(arr_chunk_len, p_data, p_chunk_crlf - p_data);
            if(!__htoi(arr_chunk_len, &ul_chunk_len))
                return HTTP_CHUNK_ERR;
    
            if(ul_chunk_len == 0)
                return HTTP_CHUNK_OK;
            
            // chunk长度位 + \r\n + chunk + \r\n
            ui_one_chunk_len = p_chunk_crlf - p_data + 2 + ul_chunk_len + 2;
            if (ui_data_len < ui_one_chunk_len)
            {
                p_comm_session->i_chunk_left_len = ui_one_chunk_len - ui_data_len;
                return HTTP_CHUNK_TODO;
            }
            else if (ui_data_len == ui_one_chunk_len)
            {
                p_comm_session->i_chunk_left_len = 0;
                return HTTP_CHUNK_TODO;
            }
            else
            {
                p_data += ui_one_chunk_len;
                ui_data_len -= ui_one_chunk_len;
                // 最后一个chunk
                if (ui_data_len == 5 && strncmp(p_data, "0\r\n\r\n", 5) == 0)
                    return HTTP_CHUNK_OK;
                
                i_chunk_left_len = 0;
                continue;
            }
        }
        else
        {
            if (ui_data_len < (unsigned int)i_chunk_left_len)
            {
                p_comm_session->i_chunk_left_len -= ui_data_len;
                return HTTP_CHUNK_TODO;
            }
            else if (ui_data_len == (unsigned int)i_chunk_left_len)
            {
                p_comm_session->i_chunk_left_len = 0;
                return HTTP_CHUNK_TODO;
            }
            else
            {
                p_data += i_chunk_left_len;
                ui_data_len -= i_chunk_left_len;
                // 最后一个chunk
                if (ui_data_len == 5 && strncmp(p_data, "0\r\n\r\n", 5) == 0)
                    return HTTP_CHUNK_OK;
                
                i_chunk_left_len = 0;
                continue;
            }
        }
    }
    
    p_comm_session->i_chunk_left_len = 0;
    return HTTP_CHUNK_OK;
}
